#!/usr/bin/perl
#
# (C) Copyright 2010- Hewlett-Packard Development Company, L.P.
# 
# $Id: instconfig.pl 18812 2012-06-19 07:17:25Z yi.wang4@hp.com $
#

use strict;
use 5.008000;
use warnings;
use POSIX;
use Getopt::Long; 
use File::Basename;
use Sys::Hostname;
use File::Copy;
use Data::Dumper;
use Storable qw/store retrieve/;

use lib '/opt/hp/nv/lib/perl';
use config ':all';
use instance ':all';
use bright ':all';

# possible error returns
use constant {
    OK => 0,
    WARN => 1,
    CRITICAL => 2,
    UNKNOWN => 3,
};

# Global variables

# common global variables
my $proceed=1;
my $done=0;

my $myscriptname = "instconfig";
my $status_file="$nvpath/instance/status/" . $myscriptname . '.stat';
my $revision = '$Revision: 18812 $';
$revision =~ s/\$//g;

# script specific globals
# possible return values from sub
my $GOOD = 1;
my $BAD = 0;

# Bright Image directories
my $IMAGEDIR = "/cm/images";

# Path to instance.conf file
my $instance_conf = "$nvpath/lib/instance.conf";
my $instance_conf_tmp = "/tmp/.instance.conf.tmp";
my $gv_inst_conf = {};
my $gv_prev_conf = undef;
# command line options
my %gv_opts;

# default values for inputs
my %DEFAULTS = (
    CLUSTERNAME => "",
    PROFILE => "/opt/hp/platform/config/xml/Profile.xml",
    USERPASSWORD => "redhat06",
    SYSTEMBUILD => "M8",
    FLOATIP_INT => "172.31.2.220",
    );

BEGIN {
    # starting value of instance id
    my $instance_id_no = 0;
    #
    # This sub will generate the instance id
    # it will perform the conditional work for sliced or shared configs
    #
    sub get_instanceid
    {
        # set the instance id here!
        # for now let's use something simple
        # such as an incremental number
        $instance_id_no++;

        my $instanceid = sprintf "instance%02d", $instance_id_no;

        return $instanceid;
    }

}

# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
#
# Attention Template Users:
#  Subs marked with a header "DO NOT DELETE/DO NOT MODIfY" should not be removed as
#  these routines are required in the script
#  Subs marked with a header "CHANGE FOR YOUR NEEDS" should be modified
#  as required to suit the individual needs of your script
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

# DO NOT DELETE/DO NOT MODIFY
#
# This sub calls module routine to log informational message to log file
# after first prepending program name; timestamp will be added in
# Perl module
#
sub log_msg
{
    my $message = shift;
    my $infomessage;

    $infomessage = basename($0) . ": INFO: ". $message . "\n";
    instance_log_event($infomessage);

}

# DO NOT DELETE/DO NOT MODIFY
#
# This sub calls module routine to log warning message to log file
# after first prepending program name and Warning label; a timestamp 
# will be added in Perl module
#
sub log_warn
{
    my $message = shift;
    my $warnmessage;

    $warnmessage = basename($0) . ": WARNING: ". $message . "\n";
    instance_log_event($warnmessage);

}

# DO NOT DELETE/DO NOT MODIFY
#
# This sub calls module routine to log error message to log file
# after first prepending program name; timestamp will be added in
# Perl module; the program will then exit
#
sub logerr_n_die
{
    my $message = shift;
    my $errormessage;

    $errormessage = basename($0) . ": ERROR: ". $message . "\n";
    print STDERR $errormessage;
    
    instance_log_event($errormessage);
    exit(CRITICAL);

}

# CHANGE FOR YOUR NEEDS
#
# This sub checks for any requirements that must be met before 
# the configuration is performed.
#
sub pre_req_check
{
    return(OK);
}

# CHANGE FOR YOUR NEEDS
#
# This sub verifies that the work done is complete and in such a state 
# that it is ready for the next step to be done
#
sub post_check
{
    return(OK);

}

# CHANGE FOR YOUR NEEDS
#
# This sub checks the script status to determine whether 
# the script has already been run successfully.
# The status file could just hold "completed", for example,
# or it could hold a count of how many things were processed last time,
# e.g. number of nodes, or number of disks, etc.  So that a comparison
# can be made to see if processing must be done again.
#
sub get_status
{
       
    # Keep the open and close of the file along with the line processing

    # if we haven't yet written this file then we surely must proceed
    return($proceed) if (!-e $status_file);

    open(IFH,$status_file) || logerr_n_die("Can't open file $status_file: $!");
    while (<IFH>) # loop through the whole file
    {
        next if (/^\s*$/);
        next if (/^\#/);

        # ADD HERE THE PROCESSING THAT MAKES SENSE FOR YOUR CODE!
        # IN THIS EXAMPLE WE CHECK FOR 'completed'
        if (/completed/) {
            close(IFH);
            return($done);
        }
    }

    close(IFH);
    return($proceed);
}

# CHANGE FOR YOUR NEEDS
#
# This sub sets the script status to be examined
# the next time the script is run.
# The status file could just hold "completed", for example,
# or it could hold a count of how many things were processed last time,
# e.g. number of nodes, or number of disks, etc.  So that a comparison
# can be made to see if processing must be done again.
#
sub set_status($)
{
    my $statusmsg = shift;
    # Keep the open and close of the file
    open(OFH,">$status_file") || logerr_n_die("Can't open file $status_file: $!\n");

    # ADD HERE THE PROCESSING THAT MAKES SENSE FOR YOUR CODE!
    print OFH "$statusmsg\n";

    close(OFH);

}

# This sub will get one line from stdin, remove all spaces and trail '\n'
sub get_answer($;$)
{
    my $prompt = shift;
    my $default = shift;

    $default = "" unless (defined $default);

    $prompt .= ", or 'Q' to quit:";
    $prompt .= " [$default]" if (length($default)>0);

    print "\n$prompt\n";

    my $answer = readline(*STDIN);
    chomp($answer);
    $answer =~ s/\s//g;

    $answer = $default if (length($answer) == 0);

    if (length($answer) == 1 && $answer =~ /[qQ]/) {
        # the admin is calling it quits
        store $gv_inst_conf, $instance_conf_tmp;
        logerr_n_die("Admin requested exit.\n");
    } 

    return $answer;
}

#
# This sub will validate the format of IP addresses that
# have been provided to make sure that they are valid
#
sub validate_ip($;$)
{
my($ip, $ping) =  @_;

    return $BAD if ($ip !~ /[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/);

    # now check the individual components of the string
    foreach my $comp (split /\./,$ip) {
        # string has component out of range 0 to 255
        return $BAD if ($comp < 0 || $comp > 255);

    }

    if ($ping) {
    # need to run ping to test the ip address
        my $cmd = "/bin/ping -c 2 -w 3 $ip";
        `$cmd 2>&1`;
        if ((WIFEXITED($?) && (WEXITSTATUS($?) != 0))) {
            return $BAD;
        }

    }
    return($GOOD);
}

#
# This sub prompts for the cluster name
#
sub prompt_for_clustername
{
    for(;;) {
        my $answer = get_answer("Please enter the cluster name", 
                               $gv_prev_conf->{Cluster_Name});

        if (length($answer) > 0 && $answer =~ /^[A-Za-z][0-9A-Za-z]*$/) {

            # if format looks good, then let's compare it with 
            # what we know it to be to be sure that this is 
            # the right information
            # but we will do a case insensitive comparison
            # because someone might have capitalized the first
            # character, or all, etc. but we want to check the characters
            # since we are just writing the instance config
            # file we cannot rely on info we would get from
            # there to compare, so let's use a Bright
            # command to get the cluster name
            my $clustername = cm_GetClusterName();

            logerr_n_die("Getting cluster name via cmsh") if ($clustername eq "");
            if ($clustername =~ /$answer/i) {
                $gv_inst_conf->{Cluster_Name} = lc $answer;
                $DEFAULTS{CLUSTERNAME} = lc $answer;
                last;
            } else { 
                # something doesn't match in name -- double check
                print "Cluster name provided does not match name configured for cluster.  Please recheck your information.\n";
            } 
        } 

        print "Invalid input.\n";
        # Huh??  go to the top and ask them what we are looking for..
    }

}

#
# This sub prompts for the cluster alias IP
#
sub prompt_for_clusteraliasip
{
    for(;;) {
        my $answer = get_answer("Please enter the cluster alias IP in an xxx.yyy.zzz.aaa format", 
                              $gv_prev_conf->{Cluster_IP});

        if( validate_ip($answer, 1) == $GOOD){
            $gv_inst_conf->{Cluster_IP} = $answer;
            last;
        }
        print "Invalid input.\n";
                # Huh??  go to the top and ask them what we are looking for..
    }

}


#
# This sub prompts for the requestor of the installation
#
sub prompt_for_requestor
{
    for(;;) {
        my $answer = get_answer("Please enter name of the person making request in the format lastname,firstname", 
                              $gv_prev_conf->{Requestor_Name});

        if (length($answer) > 0 && $answer =~ /^[A-Za-z]+,[A-Za-z]+$/) {
            $gv_inst_conf->{Requestor_Name} = $answer;
            last;
        } 

        print "Invalid input.\n";
        # Huh??  go to the top and ask them what we are looking for..
    }
}

#
# This sub prompts for the phone number of the person
# making the request
#
sub prompt_for_requestorphone
{
    for(;;) {
        my $answer = get_answer("Please enter the requestor's phone number in the form (xxx)yyy-zzzz", 
                              $gv_prev_conf->{Requestor_Phone});

        if (length($answer) > 0 && $answer =~ /^\([0-9][0-9][0-9]\)[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9]$/)  {
            $gv_inst_conf->{Requestor_Phone} = $answer;
            last;
        } 

        print "Invalid input.\n";
        # Huh??  go to the top and ask them what we are looking for..
    }
}

#
# This sub prompts for the location of the xml profile file
#
sub prompt_for_profileloc
{ 
    for(;;) {
        my $answer = get_answer("Please enter the location of the xml profile file includeing the path",
                              $gv_prev_conf->{Profile}); 

        if (length($answer) > 0 && $answer =~ /^\/[A-Za-z\/]+.xml$/) {
            if (-f $answer && -s $answer) {
                $gv_inst_conf->{Profile} = $answer;
                last;
            }
        } 

        print "Invalid input.\n";
        # Huh??  go to the top and ask them what we are looking for..
    }
}

#
# This sub prompts for the cluster type
#
sub prompt_for_clustertype
{
    for(;;) {
        my $answer = get_answer("\nPlease enter the cluster type (shared/sliced)",
                              $gv_prev_conf->{Cluster_Type});

        if ($answer eq "sliced" || $answer eq "shared") {
            $gv_inst_conf->{Cluster_Type} = $answer;
            last;
        }
        print "Invalid input.\n";
        # Huh??  go to the top and ask them what we are looking for..
    }
}

#
# This sub prompts for the instance user id
#
sub prompt_for_userid
{
my ($inst_id, $count) = @_;
    my $english_count = "";

    my $instance = $gv_prev_conf->{Instance_Cfgs}->{$inst_id};

    for(;;) {
        my @words_seq = ("", "1st", "2nd", "3rd");
   
        if ($count >=1 && $count <=3) {
            $english_count = $words_seq[$count];
        } 
        else {
            $english_count = $count . "th";
        }
 
        my $user_info = {};
        if ($instance && @{$instance->{Users}} >= $count) {
            $user_info = $instance->{Users}->[$count-1];
        }

        my $answer = get_answer("Please enter $english_count user id, such as squser76, 'D' if done (no more users)", 
                              $user_info->{Name});

        # this is where we indicate that there are no more users
        # we return a 0 length string
        if (length($answer) == 1 && $answer =~ /[dD]/) {
            # the admin has no more users to input
            return("");
        } 

        if (length($answer) > 0 && $answer =~ /^[a-zA-Z][0-9A-Za-z]*$/) {
            return($answer);
        } 

        print "Invalid input.\n";
        # Huh??  go to the top and ask them what we are looking for..
    }
}

#
# This sub prompts for the instance user default password
#
sub prompt_for_userpass
{
my ($inst_id, $count) = @_;

    my $instance = $gv_prev_conf->{Instance_Cfgs}->{$inst_id};

    for(;;) {
        my $def_passwd = $DEFAULTS{USERPASSWORD};

        if ($instance && @{$instance->{Users}} >= $count) {
            my $user_info = $instance->{Users}->[$count-1];
            $def_passwd = $user_info->{Password};
        }

        my $answer = get_answer("Please enter instance user $count password",
                                $def_passwd);

        if (length($answer) > 0) {
            return($answer);
        } 

        print "Invalid input.\n";
        # Huh??  go to the top and ask them what we are looking for..
    }
}

#
# This sub prompts for the storage type of the instance user
#
sub prompt_for_storagetype
{
my ($inst_id, $count) = @_;

    my $instance = $gv_prev_conf->{Instance_Cfgs}->{$inst_id};
    
    for(;;) {
        
        my $answer = get_answer("Please enter storage type(NRAID/RAID5) of instance user $count",
                                $instance->{Storage_Type});
        
        $answer = uc($answer);
        if ($answer eq "NRAID" || $answer eq "RAID5") {
            return($answer);
        } 
        
        print "Invalid input.\n";
        # Huh??  go to the top and ask them what we are looking for..
    }
}

#
# This sub prompts for the instance user type
#
sub prompt_for_usertype
{
my($inst_id, $count) = @_;

    my $instance = $gv_prev_conf->{Instance_Cfgs}->{$inst_id};
    for(;;) {
        my $answer = get_answer("Please enter the type of instance user $count, either production or development", $instance->{User_Type});

        if ($answer eq "production" || $answer eq "development") {
            return($answer);
        }

        print "Invalid input.\n";
        # Huh??  go to the top and ask them what we are looking for..
    }
}

#
# This sub prompts for the instance system build
#
sub prompt_for_systembuild
{
my($inst_id, $count) = @_;

    my $instance = $gv_prev_conf->{Instance_Cfgs}->{$inst_id};
    for(;;) {
        my $def_sysbuild = $DEFAULTS{SYSTEMBUILD};
        if ($instance && @{$instance->{Users}} >= $count) {
            my $user_info = $instance->{Users}->[$count-1];
            $def_sysbuild = $user_info->{System_Build};
        }
        
        my $answer = get_answer("Please enter the system build of instance user $count, M7 or M8", $def_sysbuild);

        if ($answer =~ /^M(7|8)$/i) {
            return($answer);
        }

        print "Invalid input.\n";
        # Huh??  go to the top and ask them what we are looking for..
    }
}

#
# This sub prompts for the instance node range 
#
sub prompt_for_noderange
{
my ($inst_id, $count) = @_;

    my $instance = $gv_prev_conf->{Instance_Cfgs}->{$inst_id};
    for(;;) {
        my $answer = get_answer("Please enter the instance $count node range in the format nxxx-nyyy", $instance->{Node_Range});

        # if format looks good, then let's do some further checking on the node range
        if (length($answer) > 0 && $answer =~ /^n[0-9]+\-n[0-9]+$/) {
            my($first,$last) = split("-", $answer);

            # now determine if the nodes provided are valid nodes
            # we only care if the node was found and the returned
            # value was 0;
            # > cmsh -c "ds n002"
            # n002 ................ [   UP   ]
            # > echo $?
            # 0
            # > cmsh -c "ds n088"
            # n088 is invalid
            # > echo $?
            # 1
            if (! cm_IsNodeUp($first)) {
                print "Invalid node $first provided.\n";
                next;
            }

            if (! cm_IsNodeUp($last)) {
                print "Invalid node $last provided.\n";
                next;
            }

            # only string comparision is OK
            if ($first ge $last) {
                print "Invalid node range provided, the nodes must be provided in increasing order.\n";
                next;
            }

            my @nodegroups = cm_GetNodeGroup();
            logerr_n_die("Getting nodegroup from cmsh") if (scalar @nodegroups == 0);

            my ($find_first, $find_last) = (0,0);

            for (@nodegroups) {
                $find_first = 1 if (/^$first/);
                $find_last = 1 if (/$last$/);

                # node range checks out so let's return it
                return($answer) if ($find_first && $find_last);
            }
        } 

        print "Invalid input.\n";
        # Huh??  go to the top and ask them what we are looking for..
    }
}

#
# This sub prompts for the instance using Floating IP or not
#
sub prompt_for_floatip_flag
{
    my($inst_id, $count) = @_;
    my $instance = $gv_prev_conf->{Instance_Cfgs}->{$inst_id};
    for(;;) {
        my $answer = get_answer("Please enter whether to use floating ip address for user $count [Y/N]", $instance->{Float_IP_Flag});

        $answer = lc($answer);
        $answer = 'yes' if $answer eq 'y';
        $answer = 'no' if $answer eq 'n';
        if($answer =~ /^yes|no$/){
            return $answer;
        }
        print "Invalid input.\n";
    }
}

#
# This sub prompts for the instance External Floating IP address
#
sub prompt_for_ext_floatip
{
my($inst_id, $count) = @_;

    my $instance = $gv_prev_conf->{Instance_Cfgs}->{$inst_id};
    for(;;) {
        my $answer = get_answer("Please enter the external floating ip address for user $count in an xxx.yyy.zzz.aaa format", $instance->{Ext_Float_IP});

        return $answer if (validate_ip($answer) == $GOOD);

        print "Invalid input.\n";
        # Huh??  go to the top and ask them what we are looking for..
    }

}

#
# This sub prompts for the instance Internal Floating IP address
#
sub prompt_for_int_floatip
{
my ($inst_id, $count) = @_;
    my $instance = $gv_prev_conf->{Instance_Cfgs}->{$inst_id};

    my $def_int_floatip = $DEFAULTS{FLOATIP_INT};
    if ($instance->{Int_Float_IP}) {
        $def_int_floatip = $instance->{Int_Float_IP};
    }

    for(;;) {
        my $answer = get_answer("Please enter the internal floating ip address for user $count in an xxx.yyy.zzz.aaa format", $def_int_floatip);

        return $answer if (validate_ip($answer) == $GOOD);

        print "Invalid input.\n";
        # Huh??  go to the top and ask them what we are looking for..
    }

}

#
# This sub prompts for the instance BDR cluster name
#
sub prompt_for_bdrclustername
{
my ($inst_id, $count) = @_;
    my $instance = $gv_prev_conf->{Instance_Cfgs}->{$inst_id};

    my $def_clustername = $DEFAULTS{CLUSTERNAME};
    $def_clustername = $instance->{BDR_Name} if ($instance->{BDR_Name});

    for(;;) {
        my $answer = get_answer("Please enter the BDR cluster name for instance $count", 
                                $def_clustername);

        if (length($answer) > 0 && $answer =~ /^[a-zA-Z][0-9A-Za-z]*$/) {
            return($answer);
        } 

        print "Invalid input.\n";
        # Huh??  go to the top and ask them what we are looking for..
    }
}

#
# This sub prompts for the instance contact name
#
sub prompt_for_instcontact
{
my($inst_id, $count) = @_;

    my $instance = $gv_prev_conf->{Instance_Cfgs}->{$inst_id};
    for(;;) {
        my $answer = get_answer("Please enter name of the instance contact for instance $count in the format lastname,firstname", $instance->{Contact});

        if (length($answer) > 0 && $answer =~ /^[A-Za-z]+,[A-Za-z]+$/) {
            return($answer);
        } 

        print "Invalid input.\n";
        # Huh??  go to the top and ask them what we are looking for..
    }
}

#
# This sub prompts for the instance contact phone
#
sub prompt_for_instcontactphone
{
my ($inst_id, $count) = @_;
    my $instance = $gv_prev_conf->{Instance_Cfgs}->{$inst_id};

    for(;;) {
        my $answer = get_answer("Please enter the instance contact's phone number in the form (xxx)yyy-zzzz", $instance->{Contact_Phone});

        if (length($answer) > 0 && $answer =~ /^\([0-9]{3}\)[0-9]{3}\-[0-9]{4}$/)  {
            return($answer);
        } 

        print "Invalid input.\n";
        # Huh??  go to the top and ask them what we are looking for..
    }

}

#
# This sub prompts for instance special info
#
sub prompt_for_specialinfo
{
my ($inst_id, $count) = @_;
    my $instance = $gv_prev_conf->{Instance_Cfgs}->{$inst_id};

    for(;;) {
        my $answer = get_answer("Please enter any special info for instance $count, ENTER if none", $instance->{Comments});

        return $answer;
    }
}

#
# This sub prompts for the information related to each instance
# It will prompt for: user id, user id password, node range, external
# floating IP, BDR cluster name and more
#
sub prompt_for_sliced_instinfo
{
    my $usercount = shift;

    $usercount = 0 if (! defined($usercount));

    my $ref_instances = $gv_inst_conf->{Instance_Cfgs};
    for(;;) {

        # reset our hash and increment our count for the next one
        my $instance = { Users => [] };
        my $user_cfg = {};
        my $instance_id = get_instanceid();
        while (defined($ref_instances->{$instance_id})) {
            $instance_id = get_instanceid();
        }

        $usercount++;

        my $userid = prompt_for_userid($instance_id, $usercount);

        # if length is 0 that indicates that we have no more users to process
        last if (length($userid) == 0);

        # first save it in order to dump to tmp file if user type a Q/q
        $gv_inst_conf->{Instance_Cfgs}->{$instance_id} = $instance;

        $user_cfg->{Name} = $userid;

        $user_cfg->{Password} = prompt_for_userpass($instance_id, $usercount);

        push @{$instance->{Users}}, $user_cfg;

        $instance->{User_Type} = prompt_for_usertype($instance_id, $usercount);
        $instance->{System_Build} = prompt_for_systembuild($instance_id, $usercount);
        $instance->{Node_Range} = prompt_for_noderange($instance_id, $usercount);
        #$instance->{Storage_Type} = prompt_for_storagetype($instance_id, $usercount);
        $instance->{Storage_Type} = 'NRAID'; # set nraid as default since raid5 work is not ready

        $instance->{Float_IP_Flag} = prompt_for_floatip_flag($instance_id, $usercount);

        if ($instance->{Float_IP_Flag} ne "no") {
            $instance->{Ext_Float_IP} = prompt_for_ext_floatip($instance_id, $usercount);
            $instance->{Int_Float_IP} = prompt_for_int_floatip($instance_id, $usercount);

        }

        $instance->{BDR_Name} = prompt_for_bdrclustername($instance_id, $usercount);
        $instance->{Contact} =  prompt_for_instcontact($instance_id, $usercount);
        $instance->{Contact_Phone} = prompt_for_instcontactphone($instance_id, $usercount);
        $instance->{Comments} = prompt_for_specialinfo($instance_id, $usercount);

    }
}

#
# This sub prompts for the information related to each instance
# It will prompt for: user id, user id password, node range, external
# floating IP, BDR cluster name and more
#
sub prompt_for_shared_instinfo
{
    my $usercount = 0;

    # initialize the hash for our first use but not again
    my $instance = { Users => [] };
    my $instance_id = get_instanceid();
    $gv_inst_conf->{Instance_Cfgs}->{$instance_id} = $instance;

    for(;;) {

        # don't reset the hash cause we will reuse values for
        # each new user since they will share the same instance

        $usercount++;
        my $user_cfg = {};

        my $userid = prompt_for_userid($instance_id, $usercount);

        if (length($userid) == 0) {
            # You must provide at least one user for a shared system
            if ($usercount == 1) {
                print "No user input provided.  You must provide information for at least one user/instance.\n";
                $usercount--;
                next;
            }
            else {
                last;
            }
        }
        else {
            # first save it in order to dump to tmp file if user type a Q/q
            $gv_inst_conf->{Instance_Cfgs}->{$instance_id} = $instance;

            $user_cfg->{Name} = $userid;
        }

        $user_cfg->{Password} = prompt_for_userpass($instance_id, $usercount);

        push @{$instance->{Users}}, $user_cfg;

        # get instance information the first time through, but
        # each subsequent time we are only interested in
        # the user info (user name and password)
        if ($usercount == 1) {
            # if shared user type will always be development, so
            # don't bother asking
            $instance->{User_Type} = "development";
            $instance->{System_Build} = prompt_for_systembuild($instance_id, $usercount);
            $instance->{Node_Range} = prompt_for_noderange($instance_id, $usercount);
            #$instance->{Storage_Type} = prompt_for_storagetype($instance_id, $usercount);
            $instance->{Storage_Type} = 'NRAID'; # set nraid as default since raid5 work is not ready

            # no floating ip for shared system
            $instance->{Float_IP_Flag} = "no";

            $instance->{BDR_Name} = prompt_for_bdrclustername($instance_id, $usercount);
            $instance->{Contact} =  prompt_for_instcontact($instance_id, $usercount);
            $instance->{Contact_Phone} = prompt_for_instcontactphone($instance_id, $usercount);
            $instance->{Comments} = prompt_for_specialinfo($instance_id, $usercount);
        }
    }
    
}

sub check_users
{
    my $ref_instances = shift;

    my @user_list = ();
    for my $inst_id (keys %{$ref_instances}) {
        my $instance = $ref_instances->{$inst_id};
        for my $user (@{$instance->{Users}}) {
            push @user_list, $user->{Name};
        }
    }
    # if only one user id was provided then there is nothing to validate so we can return
    return if (@user_list == 1);

    my %seen = ();
    my @dup_array = ();
    
    # determine whether we have duplicates
    for my $user (@user_list) { 
        unless ($seen{$user}) {
            # this is the first time we have seen it..
            $seen{$user} = 1;
        } else {
            push @dup_array, $user;
        }
    }

    # if we have any duplicates then let's generate an error and logerr_n_die!!
    if (scalar(@dup_array) > 0) {

        # tell the admin that this is bad... then die..
        logerr_n_die("User ids @dup_array have been duplicated.  The $instance_conf file will not be created.  We cannot continue!\n");
    }
}

sub check_noderange
{
    my $ref_instances = shift;
    my %seen = ();
    my @overlap_array = ();
        
    for my $inst_id (keys %{$ref_instances}) {
        my $instance = $ref_instances->{$inst_id};
            
        my ($first, $last) = split /-/, $instance->{Node_Range};

        for my $node ($first .. $last) { 
            unless ($seen{$node}) {
                # this is the first time we have seen it..
                $seen{$node} = 1;
            } 
            else {
                push @overlap_array, $node;
            }
        }
    }

    # if we have any duplicates then let's generate an error and logerr_n_die!!
    if (@overlap_array > 0) {
        # tell the admin that this is bad... then die..
        logerr_n_die("The following nodes @overlap_array can be found in more than one instance.  The $instance_conf file will not be created.  We cannot continue!\n");
    }
}

sub check_bdrnames
{
    my $ref_instances = shift;

    my %seen = ();
    my @dup_array = ();

    # loop through user info and ensure that there are no duplicates.
    # first create an array with all of the bdrnames
    for my $inst_id (keys %{$ref_instances}) {
        my $instance = $ref_instances->{$inst_id};
        my $bdr_name = $instance->{BDR_Name};

        unless ($seen{$bdr_name}) {
            # this is the first time we have seen it..
            $seen{$bdr_name} = 1;
        } 
        else {
            push @dup_array, $bdr_name;
        }
    }

    # if we have any duplicates then let's generate an error and logerr_n_die!!
    if (@dup_array > 0) {
        # tell the admin that this is bad... then die..
        logerr_n_die("BDR cluster names @dup_array have been duplicated.  The $instance_conf file will not be created.  We cannot continue!\n");
    }
}

sub check_ext_floatip
{
    my $ref_instances = shift;

    my %seen = ();
    my @dup_array = ();

    # loop through user info and ensure that there are no duplicates.
    # first create an array with all of the bdrnames
    for my $inst_id (keys %{$ref_instances}) {
        my $instance = $ref_instances->{$inst_id};
        
        if ($instance->{Float_IP_Flag} eq "yes") {
            my $ip = $instance->{Ext_Float_IP};

            unless ($seen{$ip}) {
                # this is the first time we have seen it..
                $seen{$ip} = 1;
            } 
            else {
                push @dup_array, $ip;
            }
        }
    }

    # if we have any duplicates then let's generate an error and logerr_n_die!!
    if (@dup_array > 0) {
        # tell the admin that this is bad... then die..
        logerr_n_die("External floating IP addresses @dup_array have been duplicated.  The $instance_conf file will not be created.  We cannot continue!\n");
    }
}

sub check_int_floatip
{
    my $ref_instances = shift;
    my %seen = ();
    my @dup_array = ();

    # loop through user info and ensure that there are no duplicates.
    # first create an array with all of the bdrnames
    for my $inst_id (keys %{$ref_instances}) {
        my $instance = $ref_instances->{$inst_id};
        
        if ($instance->{Float_IP_Flag} eq "yes") {
            my $ip = $instance->{Int_Float_IP};

            unless ($seen{$ip}) {
                # this is the first time we have seen it..
                $seen{$ip} = 1;
            } 
            else {
                push @dup_array, $ip;
            }
        }
    }

    # if we have any duplicates then let's generate an error and logerr_n_die!!
    if (@dup_array > 0) {
        # tell the admin that this is bad... then die..
        logerr_n_die("Internal floating IP addresses @dup_array have been duplicated.  The $instance_conf file will not be created.  We cannot continue!\n");
    }
}


#
# This sub tries to validate some of the input provided
#
sub validate_input
{
    # Check for duplicated users
    my $ref_instances = $gv_inst_conf->{Instance_Cfgs};
    check_users($ref_instances);

    if ($gv_inst_conf->{Cluster_Type} eq "sliced") {
        # check for overlapped node ranges
        check_noderange($ref_instances);

        # NOW let's make sure that the bdr names provided are all unique
        # let's reset our array
        check_bdrnames();

        # NOW let's make sure that the ext and int floating IPs provided 
        # are all unique
        check_ext_floatip();
        check_int_floatip();
    }
}

sub add_user_sliced
{
    # First, find all existing slices
    my $ref_instances = $gv_inst_conf->{Instance_Cfgs};
    my $usercount = scalar keys %{$ref_instances};

    prompt_for_sliced_instinfo($usercount);

}

sub add_user_shared
{
    # There should be only one instance
    my ($inst_id, $instance) = each (%{$gv_inst_conf->{Instance_Cfgs}});

    my $usercount = scalar @{$instance->{Users}};

    for(;;) {
        $usercount++;
        my $user_cfg = {};
        my $userid = prompt_for_userid($inst_id, $usercount);
        last if (length($userid) == 0); 

        $user_cfg->{Name} = $userid;
        $user_cfg->{Password} = prompt_for_userpass($inst_id, $usercount);

        push @{$instance->{Users}}, $user_cfg;
    }
}

sub del_user
{
    my $squser = shift;

    $gv_inst_conf = get_instance_conf_ref();
    my $ref_instances = $gv_inst_conf->{Instance_Cfgs};

    my $config = get_inst_config_type();

    while (my ($inst_id, $instance) = each (%{$ref_instances})) {

        for my $index (0 .. @{$instance->{Users}}-1) { # foreach user
            if ($squser eq $instance->{Users}->[$index]->{Name}) {
                if ($config eq "sliced") { 
                    # delete the slice 
                    delete($ref_instances->{$inst_id});
                }
                else { # delete the user
                    delete($instance->{Users}->[$index]);
                }
                return;
            }
        }
    }
}

# CHANGE FOR YOUR NEEDS
#
# This sub verifies that the work done is complete and in such a state 
# that it is ready for the next step to be done
#
sub perform_config
{
    # Don't read status file if add or del user
    if ($gv_opts{adduser}) {
        my $config = get_inst_config_type();

        $gv_inst_conf = get_instance_conf_ref();

        if ($config eq "sliced") {
           add_user_sliced();
        }
        else {
           add_user_shared();
        }
    }
    elsif($gv_opts{deluser}) {
        del_user($gv_opts{squser});
    }
    else {

        # Keep the following checks to determine whether the
        # script is being forced to be re-run, and to check
        # whether this script completed successfully
        my $force_flag = get_force_flag();
        my $status_value = get_status();
        return(OK) if (!$force_flag && ($status_value == $done));
        if (-e $instance_conf) {
            logerr_n_die ("Attempting to rewrite instance.conf file. Exiting.\n");
        }

        # get the hostname for this node
        my $hostname = hostname();
    
        $DEFAULTS{CLUSTERNAME} = $hostname;
        $DEFAULTS{CLUSTERNAME} =~ s/\-hn.*//;

        # retrieve last inputs
        $gv_prev_conf = retrieve($instance_conf_tmp) if (-f $instance_conf_tmp);

        # set defaults
        $gv_prev_conf->{Profile} = $DEFAULTS{PROFILE} 
            if (!$gv_prev_conf->{Profile});
        $gv_prev_conf->{Cluster_Name} = $DEFAULTS{CLUSTERNAME} 
            if (!$gv_prev_conf->{Cluster_Name});

        # Prompt for the input required to build instance.conf
        my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
        $gv_inst_conf->{Date} = sprintf "%02d/%02d/%04d", $mon+1, $mday, $year+1900;

        prompt_for_clustername();
        prompt_for_clusteraliasip();
        prompt_for_requestor();
        prompt_for_requestorphone();
        prompt_for_profileloc();
        prompt_for_clustertype();

        if ($gv_prev_conf->{Cluster_Type} 
            && $gv_prev_conf->{Cluster_Type} ne $gv_inst_conf->{Cluster_Type}) {
            # if cluster type changed, all information will be invalid
            $gv_prev_conf->{Instance_Cfgs} = {};
        }

        if ($gv_inst_conf->{Cluster_Type} eq "sliced") {
            prompt_for_sliced_instinfo();
        } 
        else {
            prompt_for_shared_instinfo();
        }
    }

    # now that we have all of the information, let's perform 
    # some simple validation of the input before creating the file
    if ($gv_opts{init} || $gv_opts{adduser}) {
        validate_input();
    }

    set_instance_conf_fileloc($instance_conf, instance::WRITE);
    set_instance_conf($gv_inst_conf);

    # now let's put the instance.conf in the images
    # get the list of image names
    my @dirs = get_image_names();

    # for each image, add this file
    foreach my $dir (@dirs) {
        my $destfile = "$IMAGEDIR/${dir}$instance_conf";
        copy($instance_conf, $destfile);
    }

    log_msg("Sync the backup headnode software image");
    cm_UpdateProvisioners();

    system "/bin/rm -f $instance_conf_tmp";

    # Keep the following code to set the status to completed
    # and return success.
    set_status("completed");
    return(OK);
}

# CHANGE FOR YOUR NEEDS
# define what your script does.
#
# usage
#
sub usage
{
print <<"EOF";

This script will create the instance.conf file.

$myscriptname --help --init/adduser/deluser --squser=<squser>

EOF

exit(UNKNOWN);
}

### Main ###

# DO NOT DELETE/DO NOT MODIFY
#
# Do not delete any of the following subroutine calls.  
# You can add more code to perform_config
#

# The only option supported right now is help
# generate usage output for anything else.
&GetOptions( \%gv_opts,
             "init",
             "adduser",
             "deluser",
             "squser=s",
             "help"=> \&usage) or usage(); 

log_msg("Starting script: revision number $revision");

if (($gv_opts{adduser} && $gv_opts{deluser}) 
   || (($gv_opts{adduser} || $gv_opts{deluser}) && $gv_opts{init})) {
    logerr_n_die("You can only specify one of the three option: init, adduser or deluser");
}

# use init as the default operation if none is defined
$gv_opts{init} = 1 if (!($gv_opts{adduser} || $gv_opts{deluser}));

if ($gv_opts{deluser} && !$gv_opts{squser}) {
    logerr_n_die("Please specify the user to delete");
}

pre_req_check;

perform_config;

post_check;

# Return success 
exit(OK);
